package net.cyndeline.math

import scala.language.implicitConversions

/**
 * A Rational object used in calculations.
 */
case class Rational(n: Int, d: Int) {
  require(d != 0)
  val numer: Int = n
  val denom: Int = if (n != 0) d else 1

  /** Constructs a new Rational from an Integer. */
  def this(n: Int) = this(n, 1)
  
  /** Adds another Rational to this one. */
  def +(that: Rational): Rational = 
    new Rational(
	  numer * that.denom + that.numer * denom,
	  denom * that.denom)
  
  /** Converts an Integer to a Rational and adds it to this one. */
  def +(i: Int): Rational = new Rational(numer + i * denom, denom)
  
  /** Subtracts another Rational from this one. */
  def -(that: Rational): Rational = new Rational(
	numer * that.denom - that.numer * denom, 
	denom * that.denom)
  
  /** Converts an Integer to a Rational and subtracts it from this one. */
  def -(i: Int): Rational = new Rational(numer - i * denom, denom)
  
  /** Multiplies another Rational with this one. */
  def *(that: Rational): Rational = 
    new Rational(numer * that.numer, denom * that.denom)
  
  /** Converts an Integer to a Rational and multiplies it with this one. */
  def *(i: Int): Rational = new Rational(numer * i, denom)
  
  /** Divides this Rational by another one. */
  def /(that: Rational): Rational =
  	new Rational(numer * that.denom, denom * that.numer)
  
  /** Converts an Integer to a Rational and divides this Rational by it. */
  def /(i: Int): Rational = new Rational(numer, denom * i)
  
  /** Checks if this Rational is less than another. */
  def <(that: Rational): Boolean =
    (this.numer.toFloat/this.denom) < (that.numer.toFloat/that.denom)
  
  /** Converts an Integer to a Rational and checks if this Rational is less than it. */
  def <(that: Int): Boolean = this < new Rational(that)
  
  /** Checks if this Rational is higher than another. */
  def >(that: Rational): Boolean = 
    (this.numer.toFloat/this.denom) > (that.numer.toFloat/that.denom)
  
  /** Converts an Integer to a Rational and checks if this Rational is higher than it. */
  def >(that: Int): Boolean = this > new Rational(that)
  
  /** Checks if this Rational is equal to another. */
  def ==(that: Rational): Boolean = this.numer == that.numer && this.denom == that.denom
  
  /** Converts an Integer to a Rational and checks if this Rational is equal to it. */
  def ==(that: Int): Boolean = this.numer == that && this.denom == 1
  
  /** Checks if this Rational is less than or equal to another. */
  def <=(that: Rational): Boolean = this < that || this == that
  
  /** Converts an Integer to a Rational and checks if this Rational is less than or equal to it. */
  def <=(that: Int): Boolean = this < new Rational(that) || this == new Rational(that)
  
  /** Checks if this Rational is higher than or equal to another. */
  def >=(that: Rational): Boolean = this > that || this == that
  
  /** Converts an Integer to a Rational and checks if this Rational is higher than or equal to it. */
  def >=(that: Int): Boolean = this > new Rational(that) || this == new Rational(that)
 
  /** Returns the highest Rational between this one and another. */
  def max(that: Rational) = if (this < that) that else this
  
  /** Returns a String representation of this Rational. */
  override def toString = if (denom == 1) numer.toString else numer + "/" + denom
  
  private def gcd(a: Int, b: Int): Int = if (b == 0) a else gcd(b, a % b)
}

object Rational {
  implicit def intToRational(x: Int) = new Rational(x)
}